package com.lagou.server;

import com.lagou.common.*;
import com.lagou.handler.RpcServerHandler;
import com.lagou.pojo.RpcRequest;
import com.lagou.pojo.RpcResponse;
import com.lagou.zookeeper.RPCRegistryHandler;
import com.lagou.zookeeper.config.ConfigKeeper;
import com.lagou.zookeeper.config.RpcServerConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class RpcServer implements InitializingBean, DisposableBean {

    @Autowired
    RpcRegistryFactory rpcRegistryFactory;

    NioEventLoopGroup bossGroup;
    NioEventLoopGroup workerGroup;

    private final RpcServerConfig config = RpcServerConfig.getInstance();

    @Override
    public void destroy() throws Exception {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 扫描暴露的RPC 接口，注册到zk接口map中

        Map<String, Class> instanceCacheMap = ProviderLoader.getInstanceCacheMap();

        this.initRpcServerConfig(instanceCacheMap);
        this.startServer();
    }

    /**
     * 启动服务端
     * @throws Exception
     */
    private void startServer() throws Exception {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new IdleStateHandler(0,0,0));
                        pipeline.addLast(new RpcDecoder(RpcRequest.class, new JSONSerializer()));
                        pipeline.addLast(new RpcEncoder(RpcResponse.class, new JSONSerializer()));
                        pipeline.addLast(new RpcServerHandler());
                    }
                });

        String ip = "127.0.0.1";
        config.setNettyHost(ip);
        int port = config.getNettyPort();
        String applicationName = config.getApplicationName();
        ChannelFuture sync = serverBootstrap.bind(ip, port).sync();

        // 延迟注册
        if(config.getDelay() > 0){
            Thread.sleep(config.getDelay());
        }

        System.out.println("============开始注册============");
        this.registry(ip,port,applicationName,config.getServices());
        System.out.println("============启动成功，ip：" + ip + ",port :" + port + "============");
        sync.channel().closeFuture().sync();
    }

    /**
     * 服务注册
     * @param ip
     * @param port
     * @param applicationName
     * @param services  注册zk服务接口
     * @throws Exception
     */
    private void registry(String ip, int port, String applicationName, Map<String, Class> services) throws Exception {
        if(MapUtils.isEmpty(services)){
            System.out.println("no service find");
            throw new RuntimeException("no service find");
        }

        RPCRegistryHandler rpcRegistryHandler = rpcRegistryFactory.getObject();
        if(null == rpcRegistryHandler){
            System.out.println("registryHandler is null");
            throw new RuntimeException("registryHandler is null");
        }

        services.entrySet().stream().forEach(stringObjectEntry-> rpcRegistryHandler.registry(stringObjectEntry.getKey(),ip,port));
    }

    /**
     * 初始化参数方法
     * @param instanceCacheMap
     */
    private void initRpcServerConfig(Map<String, Class> instanceCacheMap) {

        config.setApplicationName("rpc-provider");
        config.setNettyPort(ConfigKeeper.getInstance().getNettyPort());
        config.setDelay(3000);
        config.setServices(instanceCacheMap);
    }
}
